package cn.zhangfusheng.elasticsearch.jexl;

import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.math.NumberUtils;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

/**
 * jexl3 执行
 * @author fusheng.zhang
 * @date 2022-03-07 13:30:01
 */
public class Jexl3Util {

    /**
     * 将 str 转换成 'str'
     * @param str str
     * @return
     */
    public static String toStr(String str) {
        StringBuilder result = new StringBuilder(str.length() * 2);
        Matcher mapMatcher = Jexl3Constant.MAP_PATTERN.matcher(str);
        int index = 0;
        while (mapMatcher.find()) {
            result.append(str, index, mapMatcher.start());
            String t = mapMatcher.group(1);
            if (!NumberUtils.isDigits(t)) {
                result.append("['").append(mapMatcher.group(1)).append("']");
            } else {
                result.append(mapMatcher.group(0));
            }
            index = mapMatcher.end();
        }
        if (result.length() != 0) {
            str = result.toString();
            result.setLength(0);
        }
        index = 0;
        // 匹配正则表达式, 并替换内容
        Matcher matcher = Jexl3Constant.PREFIX_PATTERN.matcher(str);
        while (matcher.find()) {
            result.append(str, index, matcher.start());
            String prefix = matcher.group(1);
            String name = matcher.group(2);
            if (":".equals(prefix)) {
                boolean isDigits = NumberUtils.isDigits(name);
                // 按顺序访问变量
                if (isDigits) name = ':' + name;
                // 按名称访问变量
                result.append(Jexl3Constant.VAR_PREFIX).append("['").append(name).append("']");
            } else if (Jexl3Constant.KEYWORD_$.equals(prefix)) {
                // 拼出常量访问语句
                result.append(Jexl3Constant.CONST_PREFIX).append("['").append(name).append("']");
            }
            index = matcher.end(2);
        }
        result.append(str.substring(index));
        return result.toString();
    }

    /**
     * 将对象转换成: Collection 集合。
     * @param obj - 用于转换的对象
     * @return Collection 集合
     */
    public static Collection<?> asCollection(Object obj) {

        if (obj == null) {
            return Collections.EMPTY_SET; // 返回空集合
        } else if (obj.getClass().isArray()) {
            return Arrays.stream(asArray(obj)).filter(Objects::nonNull).collect(Collectors.toList());
        } else if (obj instanceof Collection<?>) {
            return ((Collection<?>) obj).stream().filter(Objects::nonNull).collect(Collectors.toList()); // List, Set, Collection 直接返回
        } else if (obj instanceof Map<?, ?>) {
            return (((Map<?, ?>) obj).entrySet()).stream().filter(Objects::nonNull).collect(Collectors.toList()); // 映射表， 返回条目的集合
        } else {
            return Collections.singletonList(obj); // 其他类型, 返回包含单个对象的集合
        }
    }

    /**
     * 将对象转换成: java.lang.Object 数组。
     * @param obj 用于转换的对象
     * @return java.lang.Object 数组
     */
    public static Object[] asArray(Object obj) {
        if (obj == null) {
            return new Object[]{}; // 返回空数组
        } else if (obj.getClass().isArray()) {
            Class<?> componentType = obj.getClass().getComponentType();
            // 转换元素为基本类型的数组
            if (componentType.isPrimitive()) {
                final int length = Array.getLength(obj);
                Object[] array = new Object[length];
                for (int index = 0; index < length; index++) {
                    array[index] = Array.get(obj, index);
                }
                return array;
            }
            // 数组直接返回
            return (Object[]) obj;
        } else {
            return new Object[]{obj}; // 其他类型, 返回包含单个对象的数组
        }
    }

    /**
     * 执行表达式
     * @param params         参数列表
     * @param constantParams 常量参数列表
     * @param scriptText     执行脚本
     * @return
     */
    public static Object execute(Map<String, Object> params, Map<String, Object> constantParams, String scriptText) {
        JexlEngine jexlEngine = new JexlBuilder().create();
        JexlScript jexlScript = jexlEngine.createScript(scriptText);
        JexlContext jexlContext = new MapContext();
        jexlContext.set(Jexl3Constant.VAR_PREFIX, params);
        jexlContext.set(Jexl3Constant.CONST_PREFIX, constantParams);
        return jexlScript.execute(jexlContext);
    }
}
